/*******************************************************************************
*  Copyright 2007 - 2013, Philip S. Considine                                  *
*  This program is free software: you can redistribute it and/or modify        *
*  it under the terms of the GNU General Public License as published by        *
*  the Free Software Foundation, either version 3 of the License, or           *
*  (at your option) any later version.                                         *
*                                                                              *
*  This program is distributed in the hope that it will be useful,             *
*  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                *
*  GNU General Public License (http://www.gnu.org/licenses/)for more details.  *
*******************************************************************************/

desc: MIDI Tool v2 Lite

//tags: MIDI processing
//author: IXix



slider7:0<-127,127,1>Velocity Shift
slider8:0<0,100,1>Random Velocity (%)
slider9:0<0,127,1>Output Velocity Min
slider10:127<0,127,1>Output Velocity Max
slider11:0<-60,60,1>Transpose (semitones)


in_pin:none
out_pin:none

////////////////////////////////////////////////////////////////////////////////////////////
@init

statNoteOff = $x80;
statNoteOn = $x90;
statPitch = $xE0;
pitchCentre = 16384;

////////////////////////////////////////////////////////////////////////////////////////////
@slider

// No fractions!
slider7 = slider7 | 0;
slider8 = slider8 | 0;
slider9 = slider9 | 0;
slider10 = slider10 | 0;
slider11 = slider11 | 0;


// Clamp to respectable values

slider7 = min(max(slider7, -127), 127);
slider8 = min(max(slider8, 0), 100);
slider9 = min(slider9, slider10);
slider10 = max(slider9, slider10);
slider11 = min(max(slider11, -60), 60);


inChannel = -1; // 0-15 is specific channel, -1 is any.
noteMin = 0;
noteMax = 127;
inVelMin = 0;
inVelMax = 127;
velMode = 0;
velScale = slider7;
velocityRand = slider8 * 0.01;
outVelMin = slider9;
outVelMax = slider10;
transpose = slider11;
pitchRand = 0;
pitchReset = 1;
outChannel = -1; // 0-15 is force specific, -1 is use original
controlMode = 0 + 1; // +1 necessary for bit masking

////////////////////////////////////////////////////////////////////////////////////////////
@block
while
(
  midirecv(offset, msg1, msg23) ?
  (
    t += 1; // Counter for random stuff
    
    // Extract message type and channel
    status = msg1 & $xF0;
    channel = msg1 & $x0F;
    
    // Is it on a channel we care about?
    (channel == inChannel || inChannel == -1)?
    (
      // Set dest channel if necessary
      outChannel > -1 ? channel = outChannel;
          
      // Is it a note event?
      (status == statNoteOn || status == statNoteOff) ?
      (
        // Extract note number and velocity
        note = msg23 & $x7F;
        velocity = msg23 >> 8;

        inVelRange = 0;

        velMode == 1 ? // Ignore notes outside velocity range
        (
          velocity > 0 ? // Zero velocity is regarded as a note off
          (
            inVelRange = (velocity < inVelMin || velocity > inVelMax) ? 0 : 1;
          );
        )
        : // Clamp input velocity to range
        (
          inVelRange = 1;
          velocity < inVelMin ? velocity = inVelMin;
          velocity > inVelMax ? velocity = inVelMax;
        );

        // Is it within our note/velocity range?
        !(note < noteMin || note > noteMax) && inVelRange ?
        (
          osc = sin(t);  // Used to make random values positive or negative 

          // Do velocity but leave fake note-off (zero velocity) events alone
          velocity > 0 ?
          (                  
            // Scale velocity by percentage, discard fractional values
            velocity = (velocity + velScale) | 0;
            
            // Randomise clamped velocity by percentage, discard fractional values
            velocityRand > 0 ? velocity = (velocity + (rand(velocity * velocityRand) * osc)) | 0;
          
            // Clamp output velocity
            velocity = min(max(velocity, outVelMin), outVelMax);
          );
          
          // Do transpose and clamp result
          note = min(max(note + transpose, 0), 127);
          
          // Generate and send pitch bend message if necessary
          pitchRand > 0 ?
          (
            pitch = pitchCentre + floor(rand(pitchCentre * pitchRand) * osc);
            midisend(offset, statPitch + channel, pitch);
          );
          
          // Pass on modified MIDI message
          midisend(offset, status + channel, note | (velocity << 8));  
        )
        : // Outside note/velocity range, pass thru
        (
          // Send pitch reset if necessary
          pitchRand > 0 && pitchReset == 1 ? midisend(offset, statPitch + channel, pitchCentre);
            
          // Pass MIDI message on
          midisend(offset, msg1, msg23);
        );
      )
      : // Not a note
      (
        controlMode & 1 ? // Pass thru on original channel if necessary
        (
          midisend(offset, msg1, msg23);
        );
        
        controlMode & 2 ? // Route to destination channel if necessary
        (
          midisend(offset, status | channel, msg23);
        );
      );
    )
    :
    (
      midisend(offset, msg1, msg23); // Not on our channel, pass thru
    );
    
    1; // Force loop to continue until all messages have been processed
  );
);

